Files and paths
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ─────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.6 ✔ purrr 0.3.4
✔ tibble 3.1.7 ✔ dplyr 1.0.9
✔ tidyr 1.2.0 ✔ stringr 1.4.0
✔ readr 2.1.2 ✔ forcats 0.5.1
── Conflicts ────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
library(ggpubr)
# set seed
set.seed(2022)
# Establish base dir
root_dir <- rprojroot::find_root(rprojroot::has_dir(".git"))
# Data directory
data_dir <- file.path(root_dir, "data")
# Analysis directory
analysis_dir <- file.path(root_dir, "analyses", "immune-deconv")
# Palettes
palette_dir <- file.path(root_dir, "figures", "palettes")
# Declare output directory
output_dir <- file.path(analysis_dir, "plots")
if (!dir.exists(output_dir)) {
dir.create(output_dir)
}
# Define input files
hist_file <- file.path(data_dir, "pbta-histologies.tsv")
palette_file <- file.path(palette_dir, "broad_histology_cancer_group_palette.tsv")
quantiseq_file <- file.path(analysis_dir, "results", "quantiseq_deconv-output.rds")
polya_file <- file.path(data_dir, "pbta-gene-expression-rsem-fpkm-collapsed.polya.rds")
stranded_file <- file.path(data_dir, "pbta-gene-expression-rsem-fpkm-collapsed.stranded.rds")
# Define output files
cg_celltypes_plot_file <- file.path(output_dir, "cell_types-cancer_groups.pdf")
subtypes_celltypes_plot_file <- file.path(output_dir, "cell_types-molecular_subtypes.pdf")
pdl1_distributions_plot_file <- file.path(output_dir, "PDL1_expression_distributions.pdf")
cd274_mb_plot_file <- file.path(output_dir, "cd274_expression_mb_subtypes.pdf")
cd8_cd4_ratio_plot_file <- file.path(output_dir, "cd8_cd4_ratio.pdf")
Read in data files:
# Read in clinical data and palette file
histologies_df <- read_tsv(hist_file, guess_max = 10000)
Rows: 2840 Columns: 37
── Column specification ────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (32): Kids_First_Biospecimen_ID, sample_id, aliquot_id, Kids_First_Participant_ID, experimen...
dbl (5): OS_days, age_last_update_days, normal_fraction, tumor_fraction, tumor_ploidy
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
palette_df <- readr::read_tsv(palette_file)
Rows: 61 Columns: 9
── Column specification ────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (8): broad_histology, cancer_group, broad_histology_display, cancer_group_display, broad_his...
dbl (1): broad_histology_order
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Read in analysis data
quantiseq <- read_rds(quantiseq_file)
polya <- read_rds(polya_file)
stranded <- read_rds(stranded_file)
The following chunk prepares data for visualization: + Prepare histology and palette for all samples. Both polya and stranded libraries were used in deconvolution. + Define cancer groups of interest to plot + Subset data to those cancer groups of interest where molecular subtypes have n>=3
#### Prepare palette/histology data data
palette_mapping_df <- histologies_df %>%
# RNA-Seq samples only, *****BOTH****** polya and stranded
filter(experimental_strategy == "RNA-Seq") %>%
# Identifiers
select(Kids_First_Biospecimen_ID,
Kids_First_Participant_ID,
sample_id,
broad_histology,
cancer_group,
molecular_subtype) %>%
# Add in hex codes & display grouping
left_join(palette_df,
by = c("broad_histology", "cancer_group")) %>%
select(Kids_First_Biospecimen_ID,
contains("broad_histology"),
contains("cancer_group"),
molecular_subtype)
#### Determine the of groups we want to visualize from those with N>=15, excluding Other
cancer_groups_of_interest <- palette_mapping_df %>%
filter(cancer_group_display != "Other", !is.na(cancer_group_display)) %>%
group_by(cancer_group_display) %>%
tally() %>%
arrange(-n) %>%
filter(n > 15) %>%
pull(cancer_group_display)
#### Array of groups we want to visualize (chose 12/14 above)
# Exclude other HGG/LGG since the patterns are similar to pilocytic/DMG
cancer_groups_of_interest <- c("Pilocytic astrocytoma",
"Diffuse midline glioma",
"Craniopharyngioma",
"Ganglioglioma",
"Ependymoma",
"Medulloblastoma",
"Schwannoma",
"Neurofibroma Plexiform",
"Other embryonal tumor",
"Atypical Teratoid Rhabdoid Tumor",
"Meningioma",
"Dysembryoplastic neuroepithelial tumor")
#### Prepare quanTIseq data --> `quantiseq_subset`
# spread and create cd8/cd4 ratio
quantiseq_spread <- quantiseq %>%
# get a column for each cell type
spread(cell_type, score) %>%
mutate(cd8_cd4_ratio = `T cell CD8+` / `T cell CD4+ (non-regulatory)`)
quantiseq_gather <- quantiseq_spread %>%
gather(cell_type, score, -c(sample, library, method))
# First, find the molecular subtypes in the of interest cancer groups AND excluding unclassified, with >=3 samples
subtypes_of_interest <- palette_mapping_df %>%
filter(cancer_group_display %in% cancer_groups_of_interest,
!(str_detect(molecular_subtype, "To be classified"))) %>%
count(molecular_subtype) %>%
filter(n >= 3) %>%
pull(molecular_subtype)
# Now, filter to relevant samples and remove uncharacterized fractions
quantiseq_subset <- quantiseq_gather %>%
left_join(palette_mapping_df, by = c("sample" = "Kids_First_Biospecimen_ID")) %>%
filter(molecular_subtype %in% subtypes_of_interest,
cell_type != "uncharacterized cell") %>%
# Change loss --> loss here so inherited by all
mutate(molecular_subtype = ifelse(molecular_subtype == "DMG, H3 K28, TP53 loss",
"DMG, H3 K28, TP53 lost",
molecular_subtype))
#### Prepare expression data to explore PDL1 aka CD274 ---> `expression_pdl1`
# Combine polya and stranded to get expression for pdl1, while keeping a library annotation
polya_expression <- polya %>%
rownames_to_column("gene") %>%
filter(gene == "CD274") %>%
gather(-gene, key = sample, value = expression) %>%
mutate(library = "polya")
expression_pdl1 <- stranded %>%
rownames_to_column("gene") %>%
filter(gene == "CD274") %>%
gather(-gene, key = sample, value = expression) %>%
mutate(library = "stranded") %>%
bind_rows(polya_expression) %>%
as_tibble() %>%
mutate(log2_expression = log(expression +1, 2)) %>%
select(-expression)
# Faceted plot of cancer groups, cell types. Jitter points are colored by cancer group.
# Now, filter to relevant samples and remove uncharacterized fractions
quantiseq_subset_cg <- quantiseq_gather %>%
filter(cell_type != "uncharacterized cell" & cell_type != "cd8_cd4_ratio") %>%
left_join(palette_mapping_df, by = c("sample" = "Kids_First_Biospecimen_ID")) %>%
filter(cancer_group_display %in% cancer_groups_of_interest)
# We need to create a new scheme for labeling that shows wrapped cancer groups with `(n=X)`
quantiseq_subset_cg <- quantiseq_subset_cg %>%
dplyr::count(cancer_group_display, cell_type) %>%
dplyr::select(-cell_type) %>%
dplyr::distinct() %>%
dplyr::inner_join(
dplyr::select(palette_df, dplyr::contains("cancer_group"))
) %>%
dplyr::select(cancer_group_display, n, cancer_group_hex) %>%
# Create wrapped with (n=X) factor column for cancer groups
dplyr::mutate(cancer_group_display_n = stringr::str_wrap(glue::glue("{cancer_group_display} (N={n})"), 30),
cancer_group_display_n = forcats::fct_reorder(cancer_group_display_n, n, .desc=T)) %>%
dplyr::inner_join(quantiseq_subset_cg)
Joining, by = "cancer_group_display"
Joining, by = c("cancer_group_display", "cancer_group_hex")
# plot by cancer group
cg_plot <- quantiseq_subset_cg %>%
ggplot2::ggplot() +
aes(x = cell_type, y = score, color = cancer_group_hex) +
geom_jitter(width = 0.15, size = 0.7, alpha = 0.6) +
ggplot2::geom_boxplot(outlier.size = 0,
size = 0.25,
color = "black",
alpha = 0,
# remove whiskers
coef = 0) +
ggplot2::facet_wrap(~cancer_group_display_n, nrow = 2, scales = "free_y") +
ggplot2::scale_color_identity() +
ggplot2::labs(
x = "Immune cell",
y = "Estimated fraction in sample"
) +
ggpubr::theme_pubr() +
cowplot::panel_border() +
ggplot2::theme(
axis.text.x = ggplot2::element_text(size = 6, angle = 45, hjust = 1),
axis.text.y = ggplot2::element_text(size = 8),
axis.title = ggplot2::element_text(size = 10),
strip.text = ggplot2::element_text(size = 7),
axis.line = ggplot2::element_line(size = ggplot2::rel(0.5)),
axis.ticks = ggplot2::element_line(size = ggplot2::rel(0.5)),
legend.position = "none"
)
cg_plot

ggsave(cg_celltypes_plot_file, cg_plot, width = 11, height = 3.5, useDingbats=FALSE)
Visualize fraction distributions across cell types and molecular subtypes
# First, remove cd8_cd4_ratio
quantiseq_no_ratio <- quantiseq_subset %>%
filter(cell_type != "cd8_cd4_ratio")
# add an ordering to molecular subtypes based on broad_histology_display labels
quantiseq_no_ratio <- quantiseq_no_ratio %>%
select(broad_histology_display, molecular_subtype) %>%
unique() %>%
arrange(broad_histology_display, molecular_subtype) %>%
mutate(mol_subtype_order = 1:n()) %>%
#join back to df
inner_join(quantiseq_no_ratio, by = c("broad_histology_display", "molecular_subtype")) %>%
dplyr::mutate(molecular_subtype = forcats::fct_reorder(molecular_subtype, mol_subtype_order))
# Faceted plot of subtypes, cell types. Jitter points are colored by underlying cancer group.
subtypes_celltypes_plot <- ggplot(quantiseq_no_ratio) +
aes(x = molecular_subtype, y = score) +
geom_boxplot(outlier.shape = NA, color = "grey40", size = 0.2) +
geom_jitter(width = 0.15, size = 0.75, alpha = 0.6, aes(color = broad_histology_hex)) +
# change to ncol if we want this *Wide*
facet_wrap(~cell_type, ncol = 5, scales = "free_y") +
scale_color_identity() +
labs(
x = "Molecular subtype of tumor sample",
y = "Estimated fraction in sample"
) +
ggpubr::theme_pubr() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 4),
axis.title = element_text(size = rel(0.7)),
axis.text.y = element_text(size = rel(0.7)),
strip.text = element_text(size = rel(0.7)),
axis.line = element_line(size = rel(0.7)),
axis.ticks = element_line(size = rel(0.7)))
# Add legend
cg_color_df <- quantiseq_no_ratio %>%
select(broad_histology_hex, broad_histology) %>%
distinct()
cg_color_list <- cg_color_df$broad_histology_hex
names(cg_color_list) <- cg_color_df$broad_histology_display
Warning: Unknown or uninitialised column: `broad_histology_display`.
legend_plot <- ggplot(quantiseq_no_ratio) +
aes(x = score, y = score, color = broad_histology_display) +
geom_point() +
scale_color_manual(name = "Broad Histology", values = cg_color_list) +
theme_pubr() +
# Need to make text very small
theme(
legend.text = element_text(size = 6),
legend.title = element_text(size = 8)
)
legend_panel <- cowplot::get_legend(legend_plot)
full_plot <- cowplot::plot_grid(subtypes_celltypes_plot, legend_panel,
nrow = 2,
rel_heights = c(1, 0.1))
full_plot

ggsave(subtypes_celltypes_plot_file, full_plot, width = 11, height = 3.5, useDingbats=FALSE)
Visualize PDL1 (CD274) expression
In this reference, high levels of both CD8+ and PDL1 can mean good candidate for immunotherapy, and high levels of PDL1 with low levels of CD8+ have been associated with poor prognosis.
In our data, CD8+ fractions are very low, at most 1.5% in an HGG H3 WT sample. Distributions of CD8+ fractions also show extremely low variance, except for MB subtypes (relatively speaking). Therefore, we examine here the relationship between expression and fraction for MB specifically.
First, we explore the relationship between variables with a scatterplot. Because there is very little variation in CD8+, this is not terribly informative.
# Merge and subset data to get log2 expression of PDL1 and CD8 fractions
pdl1_cd8 <- expression_pdl1 %>%
inner_join(quantiseq_subset) %>%
select(log2_expression, cell_type, library, score, molecular_subtype, cancer_group_display, cancer_group_hex) %>%
filter(cell_type == "T cell CD8+",
cancer_group_display != "Other")
Joining, by = c("sample", "library")
# scatterplot of CD8 fractions and PDL1 expression.
ggplot(pdl1_cd8) +
aes(x = score, y = log2_expression, color = library) +
geom_jitter() +
facet_wrap(~molecular_subtype, nrow = 5) +
labs(x = "CD8+ fraction in sample", y = "log2 PDL1 expression") +
ggpubr::theme_pubr() +
theme(axis.text.x = element_text(hjust = 1,
size = rel(0.8),
angle = 45)) +
cowplot::panel_border()

Instead, let’s explore the PDL1 distributions specifically and export this figure:
# distributions of PDL1 expression
pdl1_distributions_plot <- ggplot(pdl1_cd8) +
#aes(x = fct_reorder(molecular_subtype,log2_expression, .desc=T),
aes(x = molecular_subtype,
y = log2_expression) +
# remove outliers
geom_boxplot(outlier.shape = NA, color = "grey40", size = 0.2) +
geom_jitter(width = 0.15, size = 0.75, alpha = 0.8, aes(color = cancer_group_hex)) +
#facet_wrap(~cancer_group_display, nrow = 2, scales = "free") +
scale_color_identity() +
labs(x = "Molecular subtype of sample",
y = "Log2 expression (FPKM) of CD274") +
ggpubr::theme_pubr() +
theme(axis.text.x = element_text(hjust = 1,
size = rel(0.6),
angle = 75))
pdl1_distributions_plot

ggsave(pdl1_distributions_plot_file, pdl1_distributions_plot, width = 7, height = 5, useDingbats=FALSE)
Visualize MB only, since that was the only significant result from the survival analyses
# This chunk should NOT run in CI.
if (!(params$is_ci == 1)) {
# select only mb samples
pdl1_cd8_mb <- pdl1_cd8 %>%
filter(cancer_group_display == "Medulloblastoma")
# Count group sizes to use in x-axis labels. Use this data frame going forward
df_counts <- pdl1_cd8_mb %>%
group_by(molecular_subtype) %>%
mutate(subtype_count = n()) %>%
ungroup() %>%
mutate(molecular_subtype = glue::glue("{molecular_subtype}\n(N = {subtype_count})"))
wilcox_df <- ggpubr::compare_means(log2_expression ~ molecular_subtype,
data = df_counts,
method = "wilcox.test")
wilcox_df
# Add p-values
wilcox_df <- wilcox_df %>%
mutate(y.position = c(0.8,
NA,
1.8,
1.0,
2.0,
2.2))
# distributions of PDL1 expression
cd274_plot_mb <- ggplot(df_counts) +
aes(x = molecular_subtype,
y = log2_expression) +
# remove outliers
geom_boxplot(outlier.shape = NA, color = "grey40", size = 0.2) +
geom_jitter(width = 0.15, size = 1.4, alpha = 0.5, aes(color = "black")) +
stat_pvalue_manual(wilcox_df, label = "p = {p.adj}")+
scale_color_identity() +
labs(x = "Molecular subtype of sample",
y = "CD274 expression (Log2 FPKM)") +
ggpubr::theme_pubr() +
theme(axis.text.x = element_text(hjust = 0.55,
size = rel(0.85)))
cd274_plot_mb
ggsave(cd274_mb_plot_file, cd274_plot_mb, width = 5, height = 5, useDingbats=FALSE)
}
Warning: Removed 1 rows containing non-finite values (stat_bracket).
Plot the ratio of CD8+/CD4+ T cells as a marker of “hot” tumor (potentially responsive to checkpoint blockade therapy)
Not much here - only medulloblastoma samples seem to have more CD8+ cells than CD4+ cells
# subset for just cd8/cd4 ratio
ratio_df <- quantiseq_subset %>%
filter(cell_type == "cd8_cd4_ratio",
cancer_group != "Other",
!is.nan(score),
!is.infinite(score)
)
# distributions of PDL1 expression
ratio_plot <- ggplot(ratio_df) +
aes(x = molecular_subtype,
y = score) +
# remove outliers
geom_boxplot(outlier.shape = NA, color = "grey40", size = 0.2) +
geom_jitter(width = 0.15, size = 0.75, alpha = 0.6, aes(color = broad_histology_hex)) +
scale_color_identity() +
labs(x = "Molecular subtype of tumor8 sample",
y = "Ratio of CD8+/CD4+ T cell fractions") +
ggpubr::theme_pubr() +
theme(axis.text.x = element_text(hjust = 1,
size = rel(0.3),
angle = 45),
axis.text.y = element_text(size = rel(0.7)),
axis.title = element_text(size = rel(0.7)),
axis.line = element_line(size = rel(0.7)),
axis.ticks = element_line(size = rel(0.7)))
ratio_plot

ggsave(cd8_cd4_ratio_plot_file, ratio_plot, width = 2.75, height = 4, useDingbats = FALSE)
Session info
sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Monterey 12.4
Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggpubr_0.4.0 forcats_0.5.1 stringr_1.4.0 dplyr_1.0.9 purrr_0.3.4 readr_2.1.2
[7] tidyr_1.2.0 tibble_3.1.7 ggplot2_3.3.6 tidyverse_1.3.1
loaded via a namespace (and not attached):
[1] lubridate_1.8.0 assertthat_0.2.1 rprojroot_2.0.3 digest_0.6.29 utf8_1.2.2
[6] R6_2.5.1 cellranger_1.1.0 backports_1.4.1 reprex_2.0.1 evaluate_0.15
[11] httr_1.4.3 pillar_1.7.0 rlang_1.0.3 readxl_1.4.0 rstudioapi_0.13
[16] car_3.1-0 jquerylib_0.1.4 rmarkdown_2.14 labeling_0.4.2 bit_4.0.4
[21] munsell_0.5.0 broom_1.0.0 compiler_4.1.2 modelr_0.1.8 xfun_0.31
[26] pkgconfig_2.0.3 htmltools_0.5.2 tidyselect_1.1.2 fansi_1.0.3 crayon_1.5.1
[31] tzdb_0.3.0 dbplyr_2.2.1 withr_2.5.0 grid_4.1.2 jsonlite_1.8.0
[36] gtable_0.3.0 lifecycle_1.0.1 DBI_1.1.3 magrittr_2.0.3 scales_1.2.0
[41] cli_3.3.0 stringi_1.7.6 vroom_1.5.7 carData_3.0-5 farver_2.1.1
[46] ggsignif_0.6.3 fs_1.5.2 xml2_1.3.3 bslib_0.3.1 ellipsis_0.3.2
[51] generics_0.1.3 vctrs_0.4.1 cowplot_1.1.1 tools_4.1.2 bit64_4.0.5
[56] glue_1.6.2 hms_1.1.1 parallel_4.1.2 abind_1.4-5 fastmap_1.1.0
[61] yaml_2.3.5 colorspace_2.0-3 rstatix_0.7.0 rvest_1.0.2 knitr_1.39
[66] haven_2.5.0 sass_0.4.1
LS0tCnRpdGxlOiAiVmlzdWFsaXphdGlvbiBvZiBgcXVhblRJc2VxYCBkZWNvbnZvbHV0aW9uIHJlc3VsdHMiCmF1dGhvcjogIlNKIFNwaWVsbWFuIChBTFNGIENDREwpIGFuZCBKbyBMeW5uZSBSb2tpdGEgKEQzYikiCmRhdGU6ICIyMDIyIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQpwYXJhbXM6CiAgaXNfY2k6IDAKLS0tCgo8YnI+PGJyPjxicj48YnI+CgojIyMjIEZpbGVzIGFuZCBwYXRocwoKCmBgYHtyIHNldHVwfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3B1YnIpCgojIHNldCBzZWVkCnNldC5zZWVkKDIwMjIpCgojIEVzdGFibGlzaCBiYXNlIGRpcgpyb290X2RpciA8LSBycHJvanJvb3Q6OmZpbmRfcm9vdChycHJvanJvb3Q6Omhhc19kaXIoIi5naXQiKSkKCiMgRGF0YSBkaXJlY3RvcnkKZGF0YV9kaXIgPC0gZmlsZS5wYXRoKHJvb3RfZGlyLCAiZGF0YSIpCgojIEFuYWx5c2lzIGRpcmVjdG9yeQphbmFseXNpc19kaXIgPC0gZmlsZS5wYXRoKHJvb3RfZGlyLCAiYW5hbHlzZXMiLCAiaW1tdW5lLWRlY29udiIpCgojIFBhbGV0dGVzCnBhbGV0dGVfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwgImZpZ3VyZXMiLCAicGFsZXR0ZXMiKQoKIyBEZWNsYXJlIG91dHB1dCBkaXJlY3RvcnkKb3V0cHV0X2RpciA8LSBmaWxlLnBhdGgoYW5hbHlzaXNfZGlyLCAicGxvdHMiKQppZiAoIWRpci5leGlzdHMob3V0cHV0X2RpcikpIHsKICBkaXIuY3JlYXRlKG91dHB1dF9kaXIpCn0KCiMgRGVmaW5lIGlucHV0IGZpbGVzCmhpc3RfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJwYnRhLWhpc3RvbG9naWVzLnRzdiIpCnBhbGV0dGVfZmlsZSA8LSBmaWxlLnBhdGgocGFsZXR0ZV9kaXIsICJicm9hZF9oaXN0b2xvZ3lfY2FuY2VyX2dyb3VwX3BhbGV0dGUudHN2IikKcXVhbnRpc2VxX2ZpbGUgPC0gZmlsZS5wYXRoKGFuYWx5c2lzX2RpciwgInJlc3VsdHMiLCAicXVhbnRpc2VxX2RlY29udi1vdXRwdXQucmRzIikKcG9seWFfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJwYnRhLWdlbmUtZXhwcmVzc2lvbi1yc2VtLWZwa20tY29sbGFwc2VkLnBvbHlhLnJkcyIpCnN0cmFuZGVkX2ZpbGUgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAicGJ0YS1nZW5lLWV4cHJlc3Npb24tcnNlbS1mcGttLWNvbGxhcHNlZC5zdHJhbmRlZC5yZHMiKQoKCiMgRGVmaW5lIG91dHB1dCBmaWxlcwpjZ19jZWxsdHlwZXNfcGxvdF9maWxlIDwtIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiY2VsbF90eXBlcy1jYW5jZXJfZ3JvdXBzLnBkZiIpCnN1YnR5cGVzX2NlbGx0eXBlc19wbG90X2ZpbGUgPC0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJjZWxsX3R5cGVzLW1vbGVjdWxhcl9zdWJ0eXBlcy5wZGYiKQpwZGwxX2Rpc3RyaWJ1dGlvbnNfcGxvdF9maWxlICA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlBETDFfZXhwcmVzc2lvbl9kaXN0cmlidXRpb25zLnBkZiIpCmNkMjc0X21iX3Bsb3RfZmlsZSA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgImNkMjc0X2V4cHJlc3Npb25fbWJfc3VidHlwZXMucGRmIikKY2Q4X2NkNF9yYXRpb19wbG90X2ZpbGUgPC0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJjZDhfY2Q0X3JhdGlvLnBkZiIpCmBgYAoKUmVhZCBpbiBkYXRhIGZpbGVzOgoKYGBge3IgcmVhZH0KIyBSZWFkIGluIGNsaW5pY2FsIGRhdGEgYW5kIHBhbGV0dGUgZmlsZQpoaXN0b2xvZ2llc19kZiA8LSByZWFkX3RzdihoaXN0X2ZpbGUsIGd1ZXNzX21heCA9IDEwMDAwKQpwYWxldHRlX2RmIDwtIHJlYWRyOjpyZWFkX3RzdihwYWxldHRlX2ZpbGUpCgojIFJlYWQgaW4gYW5hbHlzaXMgZGF0YQpxdWFudGlzZXEgPC0gcmVhZF9yZHMocXVhbnRpc2VxX2ZpbGUpCnBvbHlhIDwtIHJlYWRfcmRzKHBvbHlhX2ZpbGUpCnN0cmFuZGVkIDwtIHJlYWRfcmRzKHN0cmFuZGVkX2ZpbGUpCmBgYAoKVGhlIGZvbGxvd2luZyBjaHVuayBwcmVwYXJlcyBkYXRhIGZvciB2aXN1YWxpemF0aW9uOgorIFByZXBhcmUgaGlzdG9sb2d5IGFuZCBwYWxldHRlIGZvciBfYWxsXyBzYW1wbGVzLiBCb3RoIHBvbHlhIGFuZCBzdHJhbmRlZCBsaWJyYXJpZXMgd2VyZSB1c2VkIGluIGRlY29udm9sdXRpb24uCisgRGVmaW5lIGNhbmNlciBncm91cHMgb2YgaW50ZXJlc3QgdG8gcGxvdAorIFN1YnNldCBkYXRhIHRvIHRob3NlIGNhbmNlciBncm91cHMgb2YgaW50ZXJlc3Qgd2hlcmUgbW9sZWN1bGFyIHN1YnR5cGVzIGhhdmUgbj49MwoKYGBge3IgcHJlcF9wYWxldHRlfQojIyMjIFByZXBhcmUgcGFsZXR0ZS9oaXN0b2xvZ3kgZGF0YSBkYXRhCnBhbGV0dGVfbWFwcGluZ19kZiA8LSBoaXN0b2xvZ2llc19kZiAlPiUKICAjIFJOQS1TZXEgc2FtcGxlcyBvbmx5LCAqKioqKkJPVEgqKioqKiogcG9seWEgYW5kIHN0cmFuZGVkCiAgZmlsdGVyKGV4cGVyaW1lbnRhbF9zdHJhdGVneSA9PSAiUk5BLVNlcSIpICU+JQogICMgSWRlbnRpZmllcnMKICBzZWxlY3QoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwKICAgICAgICAgS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwKICAgICAgICAgc2FtcGxlX2lkLAogICAgICAgICBicm9hZF9oaXN0b2xvZ3ksCiAgICAgICAgIGNhbmNlcl9ncm91cCwgCiAgICAgICAgIG1vbGVjdWxhcl9zdWJ0eXBlKSAlPiUKICAjIEFkZCBpbiBoZXggY29kZXMgJiBkaXNwbGF5IGdyb3VwaW5nCiAgbGVmdF9qb2luKHBhbGV0dGVfZGYsCiAgICAgICAgICAgIGJ5ID0gYygiYnJvYWRfaGlzdG9sb2d5IiwgImNhbmNlcl9ncm91cCIpKSAlPiUKICBzZWxlY3QoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwKICAgICAgICAgY29udGFpbnMoImJyb2FkX2hpc3RvbG9neSIpLCAKICAgICAgICAgY29udGFpbnMoImNhbmNlcl9ncm91cCIpLAogICAgICAgICBtb2xlY3VsYXJfc3VidHlwZSkKCiMjIyMgRGV0ZXJtaW5lIHRoZSBvZiBncm91cHMgd2Ugd2FudCB0byB2aXN1YWxpemUgZnJvbSB0aG9zZSB3aXRoIE4+PTE1LCBleGNsdWRpbmcgT3RoZXIKY2FuY2VyX2dyb3Vwc19vZl9pbnRlcmVzdCA8LSBwYWxldHRlX21hcHBpbmdfZGYgJT4lCiAgICBmaWx0ZXIoY2FuY2VyX2dyb3VwX2Rpc3BsYXkgIT0gIk90aGVyIiwgIWlzLm5hKGNhbmNlcl9ncm91cF9kaXNwbGF5KSkgJT4lCiAgZ3JvdXBfYnkoY2FuY2VyX2dyb3VwX2Rpc3BsYXkpICU+JQogIHRhbGx5KCkgJT4lCiAgYXJyYW5nZSgtbikgJT4lCiAgZmlsdGVyKG4gPiAxNSkgJT4lCiAgcHVsbChjYW5jZXJfZ3JvdXBfZGlzcGxheSkKCiMjIyMgQXJyYXkgb2YgZ3JvdXBzIHdlIHdhbnQgdG8gdmlzdWFsaXplIChjaG9zZSAxMi8xNCBhYm92ZSkKIyBFeGNsdWRlIG90aGVyIEhHRy9MR0cgc2luY2UgdGhlIHBhdHRlcm5zIGFyZSBzaW1pbGFyIHRvIHBpbG9jeXRpYy9ETUcKY2FuY2VyX2dyb3Vwc19vZl9pbnRlcmVzdCA8LSBjKCJQaWxvY3l0aWMgYXN0cm9jeXRvbWEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEaWZmdXNlIG1pZGxpbmUgZ2xpb21hIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDcmFuaW9waGFyeW5naW9tYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdhbmdsaW9nbGlvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVwZW5keW1vbWEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZWR1bGxvYmxhc3RvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNjaHdhbm5vbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5ldXJvZmlicm9tYSBQbGV4aWZvcm0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk90aGVyIGVtYnJ5b25hbCB0dW1vciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXR5cGljYWwgVGVyYXRvaWQgUmhhYmRvaWQgVHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1lbmluZ2lvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkR5c2VtYnJ5b3BsYXN0aWMgbmV1cm9lcGl0aGVsaWFsIHR1bW9yIikKIAojIyMjIFByZXBhcmUgcXVhblRJc2VxIGRhdGEgLS0+IGBxdWFudGlzZXFfc3Vic2V0YAojIHNwcmVhZCBhbmQgY3JlYXRlIGNkOC9jZDQgcmF0aW8KcXVhbnRpc2VxX3NwcmVhZCA8LSBxdWFudGlzZXEgJT4lCiAgIyBnZXQgYSBjb2x1bW4gZm9yIGVhY2ggY2VsbCB0eXBlCiAgc3ByZWFkKGNlbGxfdHlwZSwgc2NvcmUpICU+JQogIG11dGF0ZShjZDhfY2Q0X3JhdGlvID0gYFQgY2VsbCBDRDgrYCAvIGBUIGNlbGwgQ0Q0KyAobm9uLXJlZ3VsYXRvcnkpYCkKCnF1YW50aXNlcV9nYXRoZXIgPC0gcXVhbnRpc2VxX3NwcmVhZCAlPiUgIAogIGdhdGhlcihjZWxsX3R5cGUsIHNjb3JlLCAtYyhzYW1wbGUsIGxpYnJhcnksIG1ldGhvZCkpCgojIEZpcnN0LCBmaW5kIHRoZSBtb2xlY3VsYXIgc3VidHlwZXMgaW4gdGhlIG9mIGludGVyZXN0IGNhbmNlciBncm91cHMgQU5EIGV4Y2x1ZGluZyB1bmNsYXNzaWZpZWQsIHdpdGggPj0zIHNhbXBsZXMKc3VidHlwZXNfb2ZfaW50ZXJlc3QgPC0gcGFsZXR0ZV9tYXBwaW5nX2RmICU+JQogIGZpbHRlcihjYW5jZXJfZ3JvdXBfZGlzcGxheSAlaW4lIGNhbmNlcl9ncm91cHNfb2ZfaW50ZXJlc3QsIAogICAgICAgICAhKHN0cl9kZXRlY3QobW9sZWN1bGFyX3N1YnR5cGUsICJUbyBiZSBjbGFzc2lmaWVkIikpKSAlPiUKICBjb3VudChtb2xlY3VsYXJfc3VidHlwZSkgJT4lCiAgZmlsdGVyKG4gPj0gMykgJT4lCiAgcHVsbChtb2xlY3VsYXJfc3VidHlwZSkKCgojIE5vdywgZmlsdGVyIHRvIHJlbGV2YW50IHNhbXBsZXMgYW5kIHJlbW92ZSB1bmNoYXJhY3Rlcml6ZWQgZnJhY3Rpb25zCnF1YW50aXNlcV9zdWJzZXQgPC0gcXVhbnRpc2VxX2dhdGhlciAlPiUKICBsZWZ0X2pvaW4ocGFsZXR0ZV9tYXBwaW5nX2RmLCBieSA9IGMoInNhbXBsZSIgPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIpKSAlPiUKICBmaWx0ZXIobW9sZWN1bGFyX3N1YnR5cGUgJWluJSBzdWJ0eXBlc19vZl9pbnRlcmVzdCwgCiAgICAgICAgIGNlbGxfdHlwZSAhPSAidW5jaGFyYWN0ZXJpemVkIGNlbGwiKSAlPiUKICAjIENoYW5nZSBsb3NzIC0tPiBsb3NzIGhlcmUgc28gaW5oZXJpdGVkIGJ5IGFsbAogIG11dGF0ZShtb2xlY3VsYXJfc3VidHlwZSA9IGlmZWxzZShtb2xlY3VsYXJfc3VidHlwZSA9PSAiRE1HLCBIMyBLMjgsIFRQNTMgbG9zcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRNRywgSDMgSzI4LCBUUDUzIGxvc3QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vbGVjdWxhcl9zdWJ0eXBlKSkKCgojIyMjIFByZXBhcmUgZXhwcmVzc2lvbiBkYXRhIHRvIGV4cGxvcmUgUERMMSBha2EgQ0QyNzQgLS0tPiBgZXhwcmVzc2lvbl9wZGwxYAoKIyBDb21iaW5lIHBvbHlhIGFuZCBzdHJhbmRlZCB0byBnZXQgZXhwcmVzc2lvbiBmb3IgcGRsMSwgd2hpbGUga2VlcGluZyBhIGxpYnJhcnkgYW5ub3RhdGlvbgpwb2x5YV9leHByZXNzaW9uIDwtIHBvbHlhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZSIpICU+JQogIGZpbHRlcihnZW5lID09ICJDRDI3NCIpICU+JQogIGdhdGhlcigtZ2VuZSwga2V5ID0gc2FtcGxlLCB2YWx1ZSA9IGV4cHJlc3Npb24pICU+JQogIG11dGF0ZShsaWJyYXJ5ID0gInBvbHlhIikKCmV4cHJlc3Npb25fcGRsMSA8LSBzdHJhbmRlZCAlPiUKICByb3duYW1lc190b19jb2x1bW4oImdlbmUiKSAlPiUKICBmaWx0ZXIoZ2VuZSA9PSAiQ0QyNzQiKSAlPiUKICBnYXRoZXIoLWdlbmUsIGtleSA9IHNhbXBsZSwgdmFsdWUgPSBleHByZXNzaW9uKSAlPiUKICBtdXRhdGUobGlicmFyeSA9ICJzdHJhbmRlZCIpICU+JQogIGJpbmRfcm93cyhwb2x5YV9leHByZXNzaW9uKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBtdXRhdGUobG9nMl9leHByZXNzaW9uID0gbG9nKGV4cHJlc3Npb24gKzEsIDIpKSAlPiUKICBzZWxlY3QoLWV4cHJlc3Npb24pCmBgYAoKYGBge3IgY2FuY2VyX2dyb3Vwc19jZWxsdHlwZXNfcGxvdCwgZmlnLndpZHRoID0gMTQsIGZpZy5oZWlnaHQgPSA0LjV9CiMgRmFjZXRlZCBwbG90IG9mIGNhbmNlciBncm91cHMsIGNlbGwgdHlwZXMuIEppdHRlciBwb2ludHMgYXJlIGNvbG9yZWQgYnkgY2FuY2VyIGdyb3VwLgojIE5vdywgZmlsdGVyIHRvIHJlbGV2YW50IHNhbXBsZXMgYW5kIHJlbW92ZSB1bmNoYXJhY3Rlcml6ZWQgZnJhY3Rpb25zCnF1YW50aXNlcV9zdWJzZXRfY2cgPC0gcXVhbnRpc2VxX2dhdGhlciAlPiUKICBmaWx0ZXIoY2VsbF90eXBlICE9ICJ1bmNoYXJhY3Rlcml6ZWQgY2VsbCIgJiBjZWxsX3R5cGUgIT0gImNkOF9jZDRfcmF0aW8iKSAlPiUKICBsZWZ0X2pvaW4ocGFsZXR0ZV9tYXBwaW5nX2RmLCBieSA9IGMoInNhbXBsZSIgPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIpKSAlPiUKICBmaWx0ZXIoY2FuY2VyX2dyb3VwX2Rpc3BsYXkgJWluJSBjYW5jZXJfZ3JvdXBzX29mX2ludGVyZXN0KQoKIyBXZSBuZWVkIHRvIGNyZWF0ZSBhIG5ldyBzY2hlbWUgZm9yIGxhYmVsaW5nIHRoYXQgc2hvd3Mgd3JhcHBlZCBjYW5jZXIgZ3JvdXBzIHdpdGggYChuPVgpYApxdWFudGlzZXFfc3Vic2V0X2NnIDwtIHF1YW50aXNlcV9zdWJzZXRfY2cgJT4lCiAgZHBseXI6OmNvdW50KGNhbmNlcl9ncm91cF9kaXNwbGF5LCBjZWxsX3R5cGUpICU+JQogIGRwbHlyOjpzZWxlY3QoLWNlbGxfdHlwZSkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgZHBseXI6OmlubmVyX2pvaW4oCiAgICAgIGRwbHlyOjpzZWxlY3QocGFsZXR0ZV9kZiwgZHBseXI6OmNvbnRhaW5zKCJjYW5jZXJfZ3JvdXAiKSkKICApICU+JQogIGRwbHlyOjpzZWxlY3QoY2FuY2VyX2dyb3VwX2Rpc3BsYXksIG4sIGNhbmNlcl9ncm91cF9oZXgpICU+JQogICMgQ3JlYXRlIHdyYXBwZWQgd2l0aCAobj1YKSBmYWN0b3IgY29sdW1uIGZvciBjYW5jZXIgZ3JvdXBzCiAgZHBseXI6Om11dGF0ZShjYW5jZXJfZ3JvdXBfZGlzcGxheV9uID0gc3RyaW5ncjo6c3RyX3dyYXAoZ2x1ZTo6Z2x1ZSgie2NhbmNlcl9ncm91cF9kaXNwbGF5fSAoTj17bn0pIiksIDMwKSwKICAgICAgICAgICAgICAgIGNhbmNlcl9ncm91cF9kaXNwbGF5X24gPSBmb3JjYXRzOjpmY3RfcmVvcmRlcihjYW5jZXJfZ3JvdXBfZGlzcGxheV9uLCBuLCAuZGVzYz1UKSkgJT4lCiAgZHBseXI6OmlubmVyX2pvaW4ocXVhbnRpc2VxX3N1YnNldF9jZykKCiMgcGxvdCBieSBjYW5jZXIgZ3JvdXAKY2dfcGxvdCA8LSBxdWFudGlzZXFfc3Vic2V0X2NnICU+JQogIGdncGxvdDI6OmdncGxvdCgpICsgCiAgYWVzKHggPSBjZWxsX3R5cGUsIHkgPSBzY29yZSwgY29sb3IgPSBjYW5jZXJfZ3JvdXBfaGV4KSArIAogIGdlb21faml0dGVyKHdpZHRoID0gMC4xNSwgc2l6ZSA9IDAuNywgYWxwaGEgPSAwLjYpICsgCiAgZ2dwbG90Mjo6Z2VvbV9ib3hwbG90KG91dGxpZXIuc2l6ZSA9IDAsIAogICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC4yNSwKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAjIHJlbW92ZSB3aGlza2VycwogICAgICAgICAgICAgICAgICAgICAgICBjb2VmID0gMCkgKwogIGdncGxvdDI6OmZhY2V0X3dyYXAofmNhbmNlcl9ncm91cF9kaXNwbGF5X24sIG5yb3cgPSAyLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKyAKICBnZ3Bsb3QyOjpsYWJzKAogICAgeCA9ICJJbW11bmUgY2VsbCIsIAogICAgeSA9ICJFc3RpbWF0ZWQgZnJhY3Rpb24gaW4gc2FtcGxlIgogICkgKwogIGdncHVicjo6dGhlbWVfcHVicigpICsgCiAgY293cGxvdDo6cGFuZWxfYm9yZGVyKCkgKwogIGdncGxvdDI6OnRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDYsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBheGlzLnRleHQueSA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICBheGlzLnRpdGxlID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICBzdHJpcC50ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSA3KSwKICAgIGF4aXMubGluZSA9IGdncGxvdDI6OmVsZW1lbnRfbGluZShzaXplID0gZ2dwbG90Mjo6cmVsKDAuNSkpLAogICAgYXhpcy50aWNrcyA9IGdncGxvdDI6OmVsZW1lbnRfbGluZShzaXplID0gZ2dwbG90Mjo6cmVsKDAuNSkpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiCiAgKQoKY2dfcGxvdAoKZ2dzYXZlKGNnX2NlbGx0eXBlc19wbG90X2ZpbGUsIGNnX3Bsb3QsIHdpZHRoID0gMTEsIGhlaWdodCA9IDMuNSwgdXNlRGluZ2JhdHM9RkFMU0UpCmBgYAoKCgoKIyMjIFZpc3VhbGl6ZSBmcmFjdGlvbiBkaXN0cmlidXRpb25zIGFjcm9zcyBjZWxsIHR5cGVzIGFuZCBtb2xlY3VsYXIgc3VidHlwZXMKCmBgYHtyIHN1YnR5cGVzX2NlbGx0eXBlc19wbG90LCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CiMgRmlyc3QsIHJlbW92ZSBjZDhfY2Q0X3JhdGlvCnF1YW50aXNlcV9ub19yYXRpbyA8LSBxdWFudGlzZXFfc3Vic2V0ICU+JQogIGZpbHRlcihjZWxsX3R5cGUgIT0gImNkOF9jZDRfcmF0aW8iKQoKIyBhZGQgYW4gb3JkZXJpbmcgdG8gbW9sZWN1bGFyIHN1YnR5cGVzIGJhc2VkIG9uIGJyb2FkX2hpc3RvbG9neV9kaXNwbGF5IGxhYmVscwpxdWFudGlzZXFfbm9fcmF0aW8gPC0gcXVhbnRpc2VxX25vX3JhdGlvICU+JQogIHNlbGVjdChicm9hZF9oaXN0b2xvZ3lfZGlzcGxheSwgbW9sZWN1bGFyX3N1YnR5cGUpICU+JQogIHVuaXF1ZSgpICU+JQogIGFycmFuZ2UoYnJvYWRfaGlzdG9sb2d5X2Rpc3BsYXksIG1vbGVjdWxhcl9zdWJ0eXBlKSAlPiUKICBtdXRhdGUobW9sX3N1YnR5cGVfb3JkZXIgPSAxOm4oKSkgJT4lCiAgI2pvaW4gYmFjayB0byBkZgogIGlubmVyX2pvaW4ocXVhbnRpc2VxX25vX3JhdGlvLCBieSA9IGMoImJyb2FkX2hpc3RvbG9neV9kaXNwbGF5IiwgIm1vbGVjdWxhcl9zdWJ0eXBlIikpICU+JQogIGRwbHlyOjptdXRhdGUobW9sZWN1bGFyX3N1YnR5cGUgPSBmb3JjYXRzOjpmY3RfcmVvcmRlcihtb2xlY3VsYXJfc3VidHlwZSwgbW9sX3N1YnR5cGVfb3JkZXIpKSAKCiMgRmFjZXRlZCBwbG90IG9mIHN1YnR5cGVzLCBjZWxsIHR5cGVzLiBKaXR0ZXIgcG9pbnRzIGFyZSBjb2xvcmVkIGJ5IHVuZGVybHlpbmcgY2FuY2VyIGdyb3VwLgpzdWJ0eXBlc19jZWxsdHlwZXNfcGxvdCA8LSBnZ3Bsb3QocXVhbnRpc2VxX25vX3JhdGlvKSArCiAgYWVzKHggPSBtb2xlY3VsYXJfc3VidHlwZSwgeSA9IHNjb3JlKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEsIGNvbG9yID0gImdyZXk0MCIsIHNpemUgPSAwLjIpICsgCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjE1LCBzaXplID0gMC43NSwgYWxwaGEgPSAwLjYsIGFlcyhjb2xvciA9IGJyb2FkX2hpc3RvbG9neV9oZXgpKSArIAogICMgY2hhbmdlIHRvIG5jb2wgaWYgd2Ugd2FudCB0aGlzICpXaWRlKgogIGZhY2V0X3dyYXAofmNlbGxfdHlwZSwgbmNvbCA9IDUsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgbGFicygKICAgIHggPSAiTW9sZWN1bGFyIHN1YnR5cGUgb2YgdHVtb3Igc2FtcGxlIiwgCiAgICB5ID0gIkVzdGltYXRlZCBmcmFjdGlvbiBpbiBzYW1wbGUiCiAgKSArCiAgZ2dwdWJyOjp0aGVtZV9wdWJyKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDQpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjcpKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNykpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjcpKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IHJlbCgwLjcpKSwKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKHNpemUgPSByZWwoMC43KSkpCgojIEFkZCBsZWdlbmQKY2dfY29sb3JfZGYgPC0gcXVhbnRpc2VxX25vX3JhdGlvICU+JQogIHNlbGVjdChicm9hZF9oaXN0b2xvZ3lfaGV4LCBicm9hZF9oaXN0b2xvZ3kpICU+JQogIGRpc3RpbmN0KCkKCmNnX2NvbG9yX2xpc3QgPC0gY2dfY29sb3JfZGYkYnJvYWRfaGlzdG9sb2d5X2hleApuYW1lcyhjZ19jb2xvcl9saXN0KSA8LSBjZ19jb2xvcl9kZiRicm9hZF9oaXN0b2xvZ3lfZGlzcGxheQoKbGVnZW5kX3Bsb3QgPC0gZ2dwbG90KHF1YW50aXNlcV9ub19yYXRpbykgKyAKICBhZXMoeCA9IHNjb3JlLCB5ID0gc2NvcmUsIGNvbG9yID0gYnJvYWRfaGlzdG9sb2d5X2Rpc3BsYXkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJCcm9hZCBIaXN0b2xvZ3kiLCB2YWx1ZXMgPSBjZ19jb2xvcl9saXN0KSArIAogIHRoZW1lX3B1YnIoKSArCiAgIyBOZWVkIHRvIG1ha2UgdGV4dCB2ZXJ5IHNtYWxsCiAgdGhlbWUoCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpCiAgKQoKbGVnZW5kX3BhbmVsIDwtIGNvd3Bsb3Q6OmdldF9sZWdlbmQobGVnZW5kX3Bsb3QpCgpmdWxsX3Bsb3QgPC0gY293cGxvdDo6cGxvdF9ncmlkKHN1YnR5cGVzX2NlbGx0eXBlc19wbG90LCBsZWdlbmRfcGFuZWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSAyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoMSwgMC4xKSkKCmZ1bGxfcGxvdAoKZ2dzYXZlKHN1YnR5cGVzX2NlbGx0eXBlc19wbG90X2ZpbGUsIGZ1bGxfcGxvdCwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gMy41LCB1c2VEaW5nYmF0cz1GQUxTRSkKYGBgCgoKIyMjIFZpc3VhbGl6ZSBQREwxIChDRDI3NCkgZXhwcmVzc2lvbgoKSW4gW3RoaXNdKGh0dHBzOi8vd3d3LnRhbmRmb25saW5lLmNvbS9kb2kvZnVsbC8xMC4xMDgwLzIxNjI0MDJYLjIwMTguMTQ2MjQzMCkgcmVmZXJlbmNlLCBoaWdoIGxldmVscyBvZiBib3RoIENEOCsgYW5kIFBETDEgY2FuIG1lYW4gZ29vZCBjYW5kaWRhdGUgZm9yIGltbXVub3RoZXJhcHksIGFuZCBoaWdoIGxldmVscyBvZiBQREwxIHdpdGggbG93IGxldmVscyBvZiBDRDgrIGhhdmUgYmVlbiBhc3NvY2lhdGVkIHdpdGggcG9vciBwcm9nbm9zaXMuCgpJbiBvdXIgZGF0YSwgQ0Q4KyBmcmFjdGlvbnMgYXJlIHZlcnkgbG93LCBhdCBtb3N0IDEuNSUgaW4gYW4gSEdHIEgzIFdUIHNhbXBsZS4gRGlzdHJpYnV0aW9ucyBvZiBDRDgrIGZyYWN0aW9ucyBhbHNvIHNob3cgZXh0cmVtZWx5IGxvdyB2YXJpYW5jZSwgX2V4Y2VwdF8gZm9yIE1CIHN1YnR5cGVzIChyZWxhdGl2ZWx5IHNwZWFraW5nKS4gVGhlcmVmb3JlLCB3ZSBleGFtaW5lIGhlcmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGV4cHJlc3Npb24gYW5kIGZyYWN0aW9uIGZvciBNQiBzcGVjaWZpY2FsbHkuCgoKCkZpcnN0LCB3ZSBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXMgd2l0aCBhIHNjYXR0ZXJwbG90LiBCZWNhdXNlIHRoZXJlIGlzIHZlcnkgbGl0dGxlIHZhcmlhdGlvbiBpbiBDRDgrLCB0aGlzIGlzIG5vdCB0ZXJyaWJseSBpbmZvcm1hdGl2ZS4KCmBgYHtyIHBkbDFfc2NhdHRlcnBsb3QsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KIyBNZXJnZSBhbmQgc3Vic2V0IGRhdGEgdG8gZ2V0IGxvZzIgZXhwcmVzc2lvbiBvZiBQREwxIGFuZCBDRDggZnJhY3Rpb25zCnBkbDFfY2Q4IDwtIGV4cHJlc3Npb25fcGRsMSAlPiUKICBpbm5lcl9qb2luKHF1YW50aXNlcV9zdWJzZXQpICU+JQogIHNlbGVjdChsb2cyX2V4cHJlc3Npb24sIGNlbGxfdHlwZSwgbGlicmFyeSwgc2NvcmUsIG1vbGVjdWxhcl9zdWJ0eXBlLCBjYW5jZXJfZ3JvdXBfZGlzcGxheSwgY2FuY2VyX2dyb3VwX2hleCkgJT4lCiAgZmlsdGVyKGNlbGxfdHlwZSA9PSAiVCBjZWxsIENEOCsiLCAKICAgICAgICAgY2FuY2VyX2dyb3VwX2Rpc3BsYXkgIT0gIk90aGVyIikgCgoKIyBzY2F0dGVycGxvdCBvZiBDRDggZnJhY3Rpb25zIGFuZCBQREwxIGV4cHJlc3Npb24uCmdncGxvdChwZGwxX2NkOCkgKyAKICBhZXMoeCA9IHNjb3JlLCB5ID0gbG9nMl9leHByZXNzaW9uLCBjb2xvciA9IGxpYnJhcnkpICsgCiAgZ2VvbV9qaXR0ZXIoKSArIAogIGZhY2V0X3dyYXAofm1vbGVjdWxhcl9zdWJ0eXBlLCBucm93ID0gNSkgKwogIGxhYnMoeCA9ICJDRDgrIGZyYWN0aW9uIGluIHNhbXBsZSIsIHkgPSAibG9nMiBQREwxIGV4cHJlc3Npb24iKSArCiAgZ2dwdWJyOjp0aGVtZV9wdWJyKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMC44KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nbGUgPSA0NSkpICsKICBjb3dwbG90OjpwYW5lbF9ib3JkZXIoKSAKYGBgCgoKSW5zdGVhZCwgbGV0J3MgZXhwbG9yZSB0aGUgUERMMSBkaXN0cmlidXRpb25zIHNwZWNpZmljYWxseSBhbmQgZXhwb3J0IHRoaXMgZmlndXJlOgoKYGBge3IgcGRsMV9kaXN0cmlidXRpb25zLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KCiMgZGlzdHJpYnV0aW9ucyBvZiBQREwxIGV4cHJlc3Npb24KcGRsMV9kaXN0cmlidXRpb25zX3Bsb3QgPC0gZ2dwbG90KHBkbDFfY2Q4KSArIAogICNhZXMoeCA9IGZjdF9yZW9yZGVyKG1vbGVjdWxhcl9zdWJ0eXBlLGxvZzJfZXhwcmVzc2lvbiwgLmRlc2M9VCksCiAgYWVzKHggPSBtb2xlY3VsYXJfc3VidHlwZSwKICAgICAgeSA9IGxvZzJfZXhwcmVzc2lvbikgKwogICMgcmVtb3ZlIG91dGxpZXJzCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSwgY29sb3IgPSAiZ3JleTQwIiwgc2l6ZSA9IDAuMikgKyAKICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMTUsIHNpemUgPSAwLjc1LCBhbHBoYSA9IDAuOCwgYWVzKGNvbG9yID0gY2FuY2VyX2dyb3VwX2hleCkpICsgCiAgI2ZhY2V0X3dyYXAofmNhbmNlcl9ncm91cF9kaXNwbGF5LCBucm93ID0gMiwgc2NhbGVzID0gImZyZWUiKSArCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgbGFicyh4ID0gIk1vbGVjdWxhciBzdWJ0eXBlIG9mIHNhbXBsZSIsCiAgICAgICB5ID0gIkxvZzIgZXhwcmVzc2lvbiAoRlBLTSkgb2YgQ0QyNzQiKSArCiAgZ2dwdWJyOjp0aGVtZV9wdWJyKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMC42KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nbGUgPSA3NSkpIAoKcGRsMV9kaXN0cmlidXRpb25zX3Bsb3QKCmdnc2F2ZShwZGwxX2Rpc3RyaWJ1dGlvbnNfcGxvdF9maWxlLCBwZGwxX2Rpc3RyaWJ1dGlvbnNfcGxvdCwgd2lkdGggPSA3LCBoZWlnaHQgPSA1LCB1c2VEaW5nYmF0cz1GQUxTRSkKCmBgYApWaXN1YWxpemUgTUIgb25seSwgc2luY2UgdGhhdCB3YXMgdGhlIG9ubHkgc2lnbmlmaWNhbnQgcmVzdWx0IGZyb20gdGhlIHN1cnZpdmFsIGFuYWx5c2VzCgpgYGB7ciBwZGwxX21iLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0KIyBUaGlzIGNodW5rIHNob3VsZCBOT1QgcnVuIGluIENJLiAKaWYgKCEocGFyYW1zJGlzX2NpID09IDEpKSB7CgojIHNlbGVjdCBvbmx5IG1iIHNhbXBsZXMKcGRsMV9jZDhfbWIgPC0gcGRsMV9jZDggJT4lCiAgZmlsdGVyKGNhbmNlcl9ncm91cF9kaXNwbGF5ID09ICJNZWR1bGxvYmxhc3RvbWEiKQoKCiMgQ291bnQgZ3JvdXAgc2l6ZXMgdG8gdXNlIGluIHgtYXhpcyBsYWJlbHMuIFVzZSB0aGlzIGRhdGEgZnJhbWUgZ29pbmcgZm9yd2FyZApkZl9jb3VudHMgPC0gcGRsMV9jZDhfbWIgJT4lCiAgZ3JvdXBfYnkobW9sZWN1bGFyX3N1YnR5cGUpICU+JQogIG11dGF0ZShzdWJ0eXBlX2NvdW50ID0gbigpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKG1vbGVjdWxhcl9zdWJ0eXBlID0gZ2x1ZTo6Z2x1ZSgie21vbGVjdWxhcl9zdWJ0eXBlfVxuKE4gPSB7c3VidHlwZV9jb3VudH0pIikpCiAgCndpbGNveF9kZiA8LSBnZ3B1YnI6OmNvbXBhcmVfbWVhbnMobG9nMl9leHByZXNzaW9uIH4gbW9sZWN1bGFyX3N1YnR5cGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkZl9jb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIndpbGNveC50ZXN0IikKd2lsY294X2RmCgojIEFkZCBwLXZhbHVlcwp3aWxjb3hfZGYgPC0gd2lsY294X2RmICU+JQogbXV0YXRlKHkucG9zaXRpb24gPSBjKDAuOCwKICAgICAgICAgICAgICAgICAgICAgICBOQSwKICAgICAgICAgICAgICAgICAgICAgICAxLjgsCiAgICAgICAgICAgICAgICAgICAgICAgMS4wLAogICAgICAgICAgICAgICAgICAgICAgIDIuMCwKICAgICAgICAgICAgICAgICAgICAgICAyLjIpKQogIAojIGRpc3RyaWJ1dGlvbnMgb2YgUERMMSBleHByZXNzaW9uCmNkMjc0X3Bsb3RfbWIgPC0gZ2dwbG90KGRmX2NvdW50cykgKyAKICBhZXMoeCA9IG1vbGVjdWxhcl9zdWJ0eXBlLAogICAgICB5ID0gbG9nMl9leHByZXNzaW9uKSArCiAgIyByZW1vdmUgb3V0bGllcnMKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BLCBjb2xvciA9ICJncmV5NDAiLCBzaXplID0gMC4yKSArIAogIGdlb21faml0dGVyKHdpZHRoID0gMC4xNSwgc2l6ZSA9IDEuNCwgYWxwaGEgPSAwLjUsIGFlcyhjb2xvciA9ICJibGFjayIpKSArIAogIHN0YXRfcHZhbHVlX21hbnVhbCh3aWxjb3hfZGYsIGxhYmVsID0gInAgPSB7cC5hZGp9IikrCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgbGFicyh4ID0gIk1vbGVjdWxhciBzdWJ0eXBlIG9mIHNhbXBsZSIsCiAgICAgICB5ID0gIkNEMjc0IGV4cHJlc3Npb24gKExvZzIgRlBLTSkiKSArCiAgZ2dwdWJyOjp0aGVtZV9wdWJyKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNTUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMC44NSkpKSAKCmNkMjc0X3Bsb3RfbWIKCmdnc2F2ZShjZDI3NF9tYl9wbG90X2ZpbGUsIGNkMjc0X3Bsb3RfbWIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSwgdXNlRGluZ2JhdHM9RkFMU0UpCn0KYGBgCgojIyMgUGxvdCB0aGUgcmF0aW8gb2YgQ0Q4Ky9DRDQrIFQgY2VsbHMgYXMgYSBtYXJrZXIgb2YgImhvdCIgdHVtb3IgKHBvdGVudGlhbGx5IHJlc3BvbnNpdmUgdG8gY2hlY2twb2ludCBibG9ja2FkZSB0aGVyYXB5KQpOb3QgbXVjaCBoZXJlIC0gb25seSBtZWR1bGxvYmxhc3RvbWEgc2FtcGxlcyBzZWVtIHRvIGhhdmUgbW9yZSBDRDgrIGNlbGxzIHRoYW4gQ0Q0KyBjZWxscwoKYGBge3IgY2Q4X2NkNCByYXRpbywgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDV9CiMgc3Vic2V0IGZvciBqdXN0IGNkOC9jZDQgcmF0aW8KcmF0aW9fZGYgPC0gcXVhbnRpc2VxX3N1YnNldCAlPiUKICBmaWx0ZXIoY2VsbF90eXBlID09ICJjZDhfY2Q0X3JhdGlvIiwKICAgICAgICAgY2FuY2VyX2dyb3VwICE9ICJPdGhlciIsCiAgICAgICAgICFpcy5uYW4oc2NvcmUpLAogICAgICAgICAhaXMuaW5maW5pdGUoc2NvcmUpCiAgICAgICAgICkKCiMgZGlzdHJpYnV0aW9ucyBvZiBQREwxIGV4cHJlc3Npb24KcmF0aW9fcGxvdCA8LSBnZ3Bsb3QocmF0aW9fZGYpICsgCiAgYWVzKHggPSBtb2xlY3VsYXJfc3VidHlwZSwKICAgICAgeSA9IHNjb3JlKSArCiAgIyByZW1vdmUgb3V0bGllcnMKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BLCBjb2xvciA9ICJncmV5NDAiLCBzaXplID0gMC4yKSArIAogIGdlb21faml0dGVyKHdpZHRoID0gMC4xNSwgc2l6ZSA9IDAuNzUsIGFscGhhID0gMC42LCAgYWVzKGNvbG9yID0gYnJvYWRfaGlzdG9sb2d5X2hleCkpICsgCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgbGFicyh4ID0gIk1vbGVjdWxhciBzdWJ0eXBlIG9mIHR1bW9yOCBzYW1wbGUiLAogICAgICAgeSA9ICJSYXRpbyBvZiBDRDgrL0NENCsgVCBjZWxsIGZyYWN0aW9ucyIpICsKICBnZ3B1YnI6OnRoZW1lX3B1YnIoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IHJlbCgwLjMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IDQ1KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNykpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjcpKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IHJlbCgwLjcpKSwKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKHNpemUgPSByZWwoMC43KSkpCgpyYXRpb19wbG90CgpnZ3NhdmUoY2Q4X2NkNF9yYXRpb19wbG90X2ZpbGUsIHJhdGlvX3Bsb3QsIHdpZHRoID0gMi43NSwgaGVpZ2h0ID0gNCwgdXNlRGluZ2JhdHMgPSBGQUxTRSkKCmBgYAoKIyMjIFNlc3Npb24gaW5mbwoKYGBge3Igc2Vzc2lvbmluZm99CnNlc3Npb25JbmZvKCkKYGBg